Not only is R the very best language for statistical inference, it is rapidly evolving to accommodate the needs of data scientists. In this session, we will review some of the fundamental strengths of the R language and then discuss how R is integrating into the world of production level data science.
For the fourth year in a row, R has made it into the top 10 list of the IEEE Spectrum Magazine’s survey of general purpose programming languages. That R should be there at all is astounding! Much of R’s popularity can be attributed to the growth of data science.

A recent StackOverflow Post contained the following chart which shows the top twelve industries in the United States and the United Kingdom that generated R related questions.
The chart clearly shows that after Academia, R traffic originating from the Healthcare dominates the traffic by generated by other industries.
Moreover, another chart published in the same post shows R traffic is generally growing faster in industries where it was already popular. Academia and Healthcare are pulling away from the pack.
________________________________________________________________________________________
Our Agenda
After a short discussion of why the R Language itself is particularly well suited to data science, we will look at examples of R tools in seven categories:
* Working in R
* Manipulating Data
* Accessing Data
* Visualizing Data
* Predictive Modeling
* Machine Learning with Big Data
* Sharing Results
Agenda Details
* The R Language itself (10 min)
* A very brief history
* The Structure of R
* The R Package System
* Tools for Working in R (5 min)
* R Markdown
* R Notebooks
* Tools for Manipulating Data (20 min)
* The Tidyverse
* Tools for Accessing Data (5 min)
* Accessing Databases
* Tools for Visualizing Data (5 min) * htmlwidgets
* Predictive Modeling Tools (10 min)
* The caret package
* Tools for Big Data Platforms (30 min)
* Spark and Sparklyr
* Keras and Tensor Flow
* Tools for Sharing Results (5 min)
* Shiny
## ________________________________________________________________________________________
The R Language Itself
A Very Brief History of R
To appreciate this R’s growth, consider that R evolved from a relatively modest effort to develop an interface to Fortran statistical routines to full featured language that provides access to the world’s richest repository of statistical and machine learning algorithms. The following dates outline a short history of how this happened.
So R is a direct descendant of the S Language developed at Bell Labs by John Chambers, Rick Becker and other, but was also heavily influenced by Lisp and Scheme. Robert Gentleman and Ross Ihaka introduced Lisp like semantics to an ideas such as lexical scoping.
________________________________________________________________________________________
The Structure of the R Language
R is all about “Flow”
The R language was designed to enhance the experience of data exploration and statistical inference. The original intent of the S designers to make a convenient interface for their high powered Fortran evolved into a project to make R a full-featured language for statistical inference and modeling. People productivity is at the core of the R experience. As John Chambers puts it in his book Extending R, “One of the attractions of R has always been the ability to compute an interesting result quickly.” Ease of use and the ability to stay in the “flow” of an analysis is more important than computational efficiency.
R is a functional, object-based language. The three basic principles underlying R are:
* Everything that exists in R is an object
* Everything that happens in R is a function call
* Interfaces to other software are part of R
Chambers elaborates on this last point as follows: “A key motivation for the original S remains important now: to give easy access to the best computations for understanding data.” We see several examples of the efforts R developers put into connecting to the best things out there later in this presentation.
Objects and Functions
The interplay of objects and functions in R is apparent in the way in which data and statistical models are packaged. For example consider a simple linear model:
x <- rnorm(100)
y <- rnorm(100)
reg <- lm(y ~ x)
summary(reg)
Call:
lm(formula = y ~ x)
Residuals:
Min 1Q Median 3Q Max
-2.891 -0.667 0.034 0.721 3.370
Coefficients:
Estimate Std. Error t value Pr(>|t|)
(Intercept) -0.011937 0.113354 -0.11 0.92
x 0.000537 0.107019 0.01 1.00
Residual standard error: 1.12 on 98 degrees of freedom
Multiple R-squared: 2.57e-07, Adjusted R-squared: -0.0102
F-statistic: 2.52e-05 on 1 and 98 DF, p-value: 0.996
As the summary above indicates, R only returns model outputs when you ask for them and methods for generating summaries tend to be parsimonious. The model object reg packages quite a bit of information about the model.
str(reg,give.attr=FALSE)
List of 12
$ coefficients : Named num [1:2] -0.03641 -0.000483
$ residuals : Named num [1:100] 0.553 -1.024 0.173 -0.41 -1.432 ...
$ effects : Named num [1:100] 0.36362 0.00479 0.19874 -0.40531 -1.49677 ...
$ rank : int 2
$ fitted.values: Named num [1:100] -0.0364 -0.0366 -0.0367 -0.0366 -0.0363 ...
$ assign : int [1:2] 0 1
$ qr :List of 5
..$ qr : num [1:100, 1:2] -10 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 0.1 ...
..$ qraux: num [1:2] 1.1 1.04
..$ pivot: int [1:2] 1 2
..$ tol : num 1e-07
..$ rank : int 2
$ df.residual : int 98
$ xlevels : Named list()
$ call : language lm(formula = y ~ x)
$ terms :Classes 'terms', 'formula' language y ~ x
$ model :'data.frame': 100 obs. of 2 variables:
..$ y: num [1:100] 0.517 -1.06 0.136 -0.447 -1.468 ...
..$ x: num [1:100] -0.0345 0.3205 0.6407 0.4404 -0.2308 ...
Functions in R can call other functions. This feature is well adapted to writing functions to estimate maximum likelihood and other statistical algorithms.
# Two different rounding options
#options(digits = 6)
round(pi,4); signif(pi,4)
[1] 3.1416
[1] 3.142
# This is the way to have one function call another function
jmean <- function(x,FUN,...){
m <- FUN(sum(x)/length(x),...)
return(m)}
x <- rnorm(100)
jmean(x,round,4); jmean(x, signif,4)
[1] 0.0143
[1] 0.01427
Missing Values
A significant advantage that R has over other scripting languages is that it has an innate mechanism for dealing with missing values: NA**
z <- c(1:3, NA)
z
[1] 1 2 3 NA
is.na(z)
[1] FALSE FALSE FALSE TRUE
Not only is is easy to identify and work with missing values in R, like here:
a <- 1:5
b <- rnorm(5)
c <- LETTERS[1:5]
dF <- data.frame(a,b,c)
dF$b[3] <- NA; dF$c[4] <- NA
dF
na.omit(dF)
R also has several packages devoted to missing value imputation including:
* Amelia
* BaBooN
* cat
* ForImp
* Hmisc
* impute
* imputeMDR
* kmi
* mi
* mice
* MImix
* missForest
* MissingDataGui
* missMDA
* mitools
* mix
* mtsdi
* norm
* pan
* robCompositions
* rrcovNA
* sbgcop * VIM
* yaImpute
* Zelig
The Data Frame
A data frame is a list in which each row may represent an observation and each column a variable, is a natural data structure for statistical analysis. This consistent, ubiquitous data structure provides R with a big technical advantage. This is no accident. Both Robert Gentleman and Ross Ihaka believed in Niklaus Wirth’s dictum: algorithms + data structures = programs.
________________________________________________________________________________________
The R Package System
There are over 11,500 packages on CRAN, R’s central repository. The algorithms in these packages comprise a transparent, documented statistical resource of great value. Searching the through CRAN is a challenge, but the CRAN Task Views lists of R packages, curated by experts and organized by application area mitigate the problem.
## ________________________________________________________________________________________
________________________________________________________________________________________
________________________________________________________________________________________
________________________________________________________________________________________
________________________________________________________________________________________
________________________________________________________________________________________
________________________________________________________________________________________
Tools for Sharing Results
LS0tCnRpdGxlOiAiUiBUb29scyBmb3IgRGF0YSBTY2llbmNlIgphdXRob3I6IEpvc2VwaCBSaWNrZXJ0CmRhdGU6IDExLzAzLzE3Cm91dHB1dDogaHRtbF9ub3RlYm9vawotLS0KIVtdKG9kc2MucG5nKQoKPGJyLz4KPGJyLz4KCk5vdCBvbmx5IGlzIFIgdGhlIHZlcnkgYmVzdCBsYW5ndWFnZSBmb3Igc3RhdGlzdGljYWwgaW5mZXJlbmNlLCBpdCBpcyByYXBpZGx5IGV2b2x2aW5nIHRvIGFjY29tbW9kYXRlIHRoZSBuZWVkcyBvZiBkYXRhIHNjaWVudGlzdHMuIEluIHRoaXMgc2Vzc2lvbiwgd2Ugd2lsbCByZXZpZXcgc29tZSBvZiB0aGUgZnVuZGFtZW50YWwgc3RyZW5ndGhzIG9mIHRoZSBSIGxhbmd1YWdlIGFuZCB0aGVuIGRpc2N1c3MgaG93IFIgaXMgaW50ZWdyYXRpbmcgaW50byB0aGUgd29ybGQgb2YgcHJvZHVjdGlvbiBsZXZlbCBkYXRhIHNjaWVuY2UuCgpGb3IgdGhlIGZvdXJ0aCB5ZWFyIGluIGEgcm93LCBSIGhhcyBtYWRlIGl0IGludG8gdGhlIFt0b3AgMTAgbGlzdF0oaHR0cHM6Ly9zcGVjdHJ1bS5pZWVlLm9yZy9jb21wdXRpbmcvc29mdHdhcmUvdGhlLTIwMTctdG9wLXByb2dyYW1taW5nLWxhbmd1YWdlcykgb2YgdGhlIElFRUUgU3BlY3RydW0gTWFnYXppbmUncyBzdXJ2ZXkgb2YgZ2VuZXJhbCBwdXJwb3NlIHByb2dyYW1taW5nIGxhbmd1YWdlcy4gVGhhdCBSIHNob3VsZCBiZSB0aGVyZSBhdCBhbGwgaXMgYXN0b3VuZGluZyEgTXVjaCBvZiBSJ3MgcG9wdWxhcml0eSBjYW4gYmUgYXR0cmlidXRlZCB0byB0aGUgZ3Jvd3RoIG9mIGRhdGEgc2NpZW5jZS4gCjxici8+ICAgCiFbXShJRUVFcmFuay5wbmcpCgpBIHJlY2VudCBbU3RhY2tPdmVyZmxvdyBQb3N0XShodHRwczovL3N0YWNrb3ZlcmZsb3cuYmxvZy8yMDE3LzEwLzEwL2ltcHJlc3NpdmUtZ3Jvd3RoLXIvKSBjb250YWluZWQgdGhlIGZvbGxvd2luZyBjaGFydCB3aGljaCBzaG93cyB0aGUgdG9wIHR3ZWx2ZSBpbmR1c3RyaWVzIGluIHRoZSBVbml0ZWQgU3RhdGVzIGFuZCB0aGUgVW5pdGVkIEtpbmdkb20gdGhhdCBnZW5lcmF0ZWQgUiByZWxhdGVkIHF1ZXN0aW9ucy4gCgohW10odmlzaXRzQnlJbmR1c3RyeS5wbmcpCgpUaGUgY2hhcnQgY2xlYXJseSBzaG93cyB0aGF0IGFmdGVyIEFjYWRlbWlhLCBSIHRyYWZmaWMgb3JpZ2luYXRpbmcgZnJvbSB0aGUgSGVhbHRoY2FyZSBkb21pbmF0ZXMgdGhlIHRyYWZmaWMgYnkgZ2VuZXJhdGVkIGJ5IG90aGVyIGluZHVzdHJpZXMuCgpNb3Jlb3ZlciwgYW5vdGhlciBjaGFydCBwdWJsaXNoZWQgaW4gdGhlIHNhbWUgcG9zdCBzaG93cyBSIHRyYWZmaWMgaXMgZ2VuZXJhbGx5IGdyb3dpbmcgZmFzdGVyIGluIGluZHVzdHJpZXMgd2hlcmUgaXQgd2FzIGFscmVhZHkgcG9wdWxhci4gQWNhZGVtaWEgYW5kIEhlYWx0aGNhcmUgYXJlIHB1bGxpbmcgYXdheSBmcm9tIHRoZSBwYWNrLgoKIVtdKGdyb3d0aEJ5SW5kdXN0cnkucG5nKQoKCiMjICpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fKiAKCiMjIE91ciBBZ2VuZGEgCkFmdGVyIGEgc2hvcnQgZGlzY3Vzc2lvbiBvZiB3aHkgdGhlIFIgTGFuZ3VhZ2UgaXRzZWxmIGlzIHBhcnRpY3VsYXJseSB3ZWxsIHN1aXRlZCB0byBkYXRhIHNjaWVuY2UsIHdlIHdpbGwgbG9vayBhdCBleGFtcGxlcyBvZiBSIHRvb2xzIGluIHNldmVuIGNhdGVnb3JpZXM6ICAgICAKKiBXb3JraW5nIGluIFIgICAKKiBNYW5pcHVsYXRpbmcgRGF0YSAgIAoqIEFjY2Vzc2luZyBEYXRhICAgCiogVmlzdWFsaXppbmcgRGF0YSAgIAoqIFByZWRpY3RpdmUgTW9kZWxpbmcgICAKKiBNYWNoaW5lIExlYXJuaW5nIHdpdGggQmlnIERhdGEgICAKKiBTaGFyaW5nIFJlc3VsdHMgICAKCioqQWdlbmRhIERldGFpbHMqKiAgICAKKiAqKlRoZSBSIExhbmd1YWdlIGl0c2VsZiAoMTAgbWluKSoqICAgCiogQSB2ZXJ5IGJyaWVmIGhpc3RvcnkgICAgCiogVGhlIFN0cnVjdHVyZSBvZiBSICAgIAoqIFRoZSBSIFBhY2thZ2UgU3lzdGVtICAgCiogKipUb29scyBmb3IgV29ya2luZyBpbiBSICg1IG1pbikqKiAgIAoqIFIgTWFya2Rvd24gICAgIAoqIFIgTm90ZWJvb2tzICAgCiogKipUb29scyBmb3IgTWFuaXB1bGF0aW5nIERhdGEgKDIwIG1pbikqKiAgIAoqIFRoZSBUaWR5dmVyc2UgICAKKiAqKlRvb2xzIGZvciBBY2Nlc3NpbmcgRGF0YSAoNSBtaW4pKiogIAoqIEFjY2Vzc2luZyBEYXRhYmFzZXMgICAgCiogKipUb29scyBmb3IgVmlzdWFsaXppbmcgRGF0YSAoNSBtaW4pKioKKiBodG1sd2lkZ2V0cyAgIAoqICoqUHJlZGljdGl2ZSBNb2RlbGluZyBUb29scyAoMTAgbWluKSoqICAgICAgCiogVGhlIGNhcmV0IHBhY2thZ2UgICAKKiAqKlRvb2xzIGZvciBCaWcgRGF0YSBQbGF0Zm9ybXMgKDMwIG1pbikqKiAgICAKKiBTcGFyayBhbmQgU3BhcmtseXIgICAKKiBLZXJhcyBhbmQgVGVuc29yIEZsb3cgICAKKiAqKlRvb2xzIGZvciBTaGFyaW5nIFJlc3VsdHMgKDUgbWluKSoqICAKKiBTaGlueSAgIAo8YnIvPgojIyAqX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXyogCgojIyBUaGUgUiBMYW5ndWFnZSBJdHNlbGYKIyMjIEEgVmVyeSBCcmllZiBIaXN0b3J5IG9mIFIKClRvIGFwcHJlY2lhdGUgdGhpcyBSJ3MgZ3Jvd3RoLCBjb25zaWRlciB0aGF0IFIgZXZvbHZlZCBmcm9tIGEgcmVsYXRpdmVseSBtb2Rlc3QgZWZmb3J0IHRvIGRldmVsb3AgYW4gaW50ZXJmYWNlIHRvIEZvcnRyYW4gc3RhdGlzdGljYWwgcm91dGluZXMgdG8gZnVsbCBmZWF0dXJlZCBsYW5ndWFnZSB0aGF0IHByb3ZpZGVzIGFjY2VzcyB0byB0aGUgd29ybGQncyByaWNoZXN0IHJlcG9zaXRvcnkgb2Ygc3RhdGlzdGljYWwgYW5kIG1hY2hpbmUgbGVhcm5pbmcgYWxnb3JpdGhtcy4gVGhlIGZvbGxvd2luZyBkYXRlcyBvdXRsaW5lIGEgc2hvcnQgaGlzdG9yeSBvZiBob3cgdGhpcyBoYXBwZW5lZC4KICAgICAgICAgICAKIVtdKFJoaXN0b3J5LnBuZykKPGJyLz4gICAgICAgICAKICAgICAgICAgIApTbyBSIGlzIGEgZGlyZWN0IGRlc2NlbmRhbnQgb2YgdGhlIFMgTGFuZ3VhZ2UgZGV2ZWxvcGVkIGF0IEJlbGwgTGFicyBieSBKb2huIENoYW1iZXJzLCBSaWNrIEJlY2tlciBhbmQgb3RoZXIsIGJ1dCB3YXMgYWxzbyBoZWF2aWx5IGluZmx1ZW5jZWQgYnkgTGlzcCBhbmQgU2NoZW1lLiBSb2JlcnQgR2VudGxlbWFuIGFuZCBSb3NzIEloYWthIGludHJvZHVjZWQgTGlzcCBsaWtlIHNlbWFudGljcyB0byBhbiBpZGVhcyBzdWNoIGFzIGxleGljYWwgc2NvcGluZy4KCiMjICpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fKiAKCiMjIyBUaGUgU3RydWN0dXJlIG9mIHRoZSBSIExhbmd1YWdlCiMjIyNSIGlzIGFsbCBhYm91dCAiRmxvdyIKClRoZSBSIGxhbmd1YWdlIHdhcyBkZXNpZ25lZCB0byBlbmhhbmNlIHRoZSBleHBlcmllbmNlIG9mIGRhdGEgZXhwbG9yYXRpb24gYW5kIHN0YXRpc3RpY2FsIGluZmVyZW5jZS4gVGhlIG9yaWdpbmFsIGludGVudCBvZiB0aGUgUyBkZXNpZ25lcnMgdG8gbWFrZSBhIGNvbnZlbmllbnQgaW50ZXJmYWNlIGZvciB0aGVpciBoaWdoIHBvd2VyZWQgRm9ydHJhbiBldm9sdmVkIGludG8gYSBwcm9qZWN0IHRvIG1ha2UgUiBhIGZ1bGwtZmVhdHVyZWQgbGFuZ3VhZ2UgZm9yIHN0YXRpc3RpY2FsIGluZmVyZW5jZSBhbmQgbW9kZWxpbmcuIFBlb3BsZSBwcm9kdWN0aXZpdHkgaXMgYXQgdGhlIGNvcmUgb2YgdGhlIFIgZXhwZXJpZW5jZS4gQXMgSm9obiBDaGFtYmVycyBwdXRzIGl0IGluIGhpcyBib29rIFtFeHRlbmRpbmcgUl0oaHR0cHM6Ly93d3cuYW1hem9uLmNvbS9FeHRlbmRpbmctUi1Kb2huLU0tQ2hhbWJlcnMvZHAvMTEzODQ2OTI3MC9yZWY9c3JfMV8xP2llPVVURjgmcWlkPTE1MDY3MTc0MjUmc3I9OC0xJmtleXdvcmRzPWV4dGVuZGluZytyKSwgKirigJxPbmUgb2YgdGhlIGF0dHJhY3Rpb25zIG9mIFIgaGFzIGFsd2F5cyBiZWVuIHRoZSBhYmlsaXR5IHRvIGNvbXB1dGUgYW4gaW50ZXJlc3RpbmcgcmVzdWx0IHF1aWNrbHkuIioqIApFYXNlIG9mIHVzZSBhbmQgdGhlIGFiaWxpdHkgdG8gc3RheSBpbiB0aGUgImZsb3ciIG9mIGFuIGFuYWx5c2lzIGlzIG1vcmUgaW1wb3J0YW50IHRoYW4gY29tcHV0YXRpb25hbCBlZmZpY2llbmN5LgoKKipSIGlzIGEgZnVuY3Rpb25hbCwgb2JqZWN0LWJhc2VkIGxhbmd1YWdlLioqIFRoZSB0aHJlZSBiYXNpYyBwcmluY2lwbGVzIHVuZGVybHlpbmcgUiBhcmU6ICAgIAoqICBFdmVyeXRoaW5nIHRoYXQgZXhpc3RzIGluIFIgaXMgYW4gb2JqZWN0ICAgIAoqICBFdmVyeXRoaW5nIHRoYXQgaGFwcGVucyBpbiBSIGlzIGEgZnVuY3Rpb24gY2FsbCAgICAKKiAgSW50ZXJmYWNlcyB0byBvdGhlciBzb2Z0d2FyZSBhcmUgcGFydCBvZiBSICAgIAoKQ2hhbWJlcnMgZWxhYm9yYXRlcyBvbiB0aGlzIGxhc3QgcG9pbnQgYXMgZm9sbG93czogKioiQSBrZXkgbW90aXZhdGlvbiBmb3IgdGhlIG9yaWdpbmFsIFMgcmVtYWlucyBpbXBvcnRhbnQgbm93OiB0byBnaXZlIGVhc3kgYWNjZXNzIHRvIHRoZSBiZXN0IGNvbXB1dGF0aW9ucyBmb3IgdW5kZXJzdGFuZGluZyBkYXRhLiIqKiBXZSBzZWUgc2V2ZXJhbCBleGFtcGxlcyBvZiB0aGUgZWZmb3J0cyBSIGRldmVsb3BlcnMgcHV0IGludG8gY29ubmVjdGluZyB0byB0aGUgYmVzdCB0aGluZ3Mgb3V0IHRoZXJlIGxhdGVyIGluIHRoaXMgcHJlc2VudGF0aW9uLgoKIyMjIyBPYmplY3RzIGFuZCBGdW5jdGlvbnMKVGhlIGludGVycGxheSBvZiBvYmplY3RzIGFuZCBmdW5jdGlvbnMgaW4gUiBpcyBhcHBhcmVudCBpbiB0aGUgd2F5IGluIHdoaWNoIGRhdGEgYW5kIHN0YXRpc3RpY2FsIG1vZGVscyBhcmUgcGFja2FnZWQuIEZvciBleGFtcGxlIGNvbnNpZGVyIGEgc2ltcGxlIGxpbmVhciBtb2RlbDoKCmBgYHtyfQp4IDwtIHJub3JtKDEwMCkKeSA8LSBybm9ybSgxMDApCgpyZWcgPC0gbG0oeSB+IHgpCnN1bW1hcnkocmVnKQpgYGAKCkFzIHRoZSBzdW1tYXJ5IGFib3ZlIGluZGljYXRlcywgUiBvbmx5IHJldHVybnMgbW9kZWwgb3V0cHV0cyB3aGVuIHlvdSBhc2sgZm9yIHRoZW0gYW5kIG1ldGhvZHMgZm9yIGdlbmVyYXRpbmcgc3VtbWFyaWVzIHRlbmQgdG8gYmUgcGFyc2ltb25pb3VzLiBUaGUgbW9kZWwgb2JqZWN0IGByZWdgIHBhY2thZ2VzIHF1aXRlIGEgYml0IG9mIGluZm9ybWF0aW9uIGFib3V0IHRoZSBtb2RlbC4KCmBgYHtyfQpzdHIocmVnLGdpdmUuYXR0cj1GQUxTRSkKCmBgYAoKCioqRnVuY3Rpb25zIGluIFIgY2FuIGNhbGwgb3RoZXIgZnVuY3Rpb25zLioqIFRoaXMgZmVhdHVyZSBpcyB3ZWxsIGFkYXB0ZWQgdG8gd3JpdGluZyBmdW5jdGlvbnMgdG8gZXN0aW1hdGUgbWF4aW11bSBsaWtlbGlob29kIGFuZCBvdGhlciBzdGF0aXN0aWNhbCBhbGdvcml0aG1zLgoKYGBge3J9CiMgVHdvIGRpZmZlcmVudCByb3VuZGluZyBvcHRpb25zCnJvdW5kKHBpLDQpOyBzaWduaWYocGksNCkgICAgIAojIFRoaXMgaXMgdGhlIHdheSB0byBoYXZlIG9uZSBmdW5jdGlvbiBjYWxsIGFub3RoZXIgZnVuY3Rpb24Kam1lYW4gPC0gZnVuY3Rpb24oeCxGVU4sLi4uKXsKICAgICAgICAgICAgICAgbSA8LSBGVU4oc3VtKHgpL2xlbmd0aCh4KSwuLi4pCiAgICAgICAgICAgICAgIHJldHVybihtKX0KCnggPC0gcm5vcm0oMTAwKQpqbWVhbih4LHJvdW5kLDQpOyBqbWVhbih4LCBzaWduaWYsNCkKCmBgYAoKIyMjIE1pc3NpbmcgVmFsdWVzCkEgc2lnbmlmaWNhbnQgYWR2YW50YWdlIHRoYXQgUiBoYXMgb3ZlciBvdGhlciBzY3JpcHRpbmcgbGFuZ3VhZ2VzIGlzIHRoYXQgaXQgaGFzIGFuIGlubmF0ZSBtZWNoYW5pc20gZm9yIGRlYWxpbmcgd2l0aCBtaXNzaW5nIHZhbHVlczogTkEqKgpgYGB7cn0KeiA8LSBjKDE6MywgTkEpCnoKaXMubmEoeikKCmBgYAoKTm90IG9ubHkgaXMgaXMgZWFzeSB0byBpZGVudGlmeSBhbmQgd29yayB3aXRoIG1pc3NpbmcgdmFsdWVzIGluIFIsIGxpa2UgaGVyZToKYGBge3J9CmEgPC0gMTo1CmIgPC0gcm5vcm0oNSkKYyA8LSBMRVRURVJTWzE6NV0KZEYgPC0gZGF0YS5mcmFtZShhLGIsYykKZEYkYlszXSA8LSBOQTsgZEYkY1s0XSA8LSBOQQpkRgpuYS5vbWl0KGRGKQpgYGAKClIgYWxzbyBoYXMgc2V2ZXJhbCBwYWNrYWdlcyBkZXZvdGVkIHRvIG1pc3NpbmcgdmFsdWUgaW1wdXRhdGlvbiBpbmNsdWRpbmc6ICAgICAgICAgCiogW0FtZWxpYV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1BbWVsaWEpICAgIAoqIFtCYUJvb05dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9QmFCb29OKSAgICAKKiBbY2F0XShodHRwczovL0NSQU4uUi1wcm9qZWN0Lm9yZy9wYWNrYWdlPWNhdCkgICAgCiogW0ZvckltcF0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1Gb3JJbXApICAgIAoqIFtIbWlzY10oaHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1IbWlzYykgICAgCiogW2ltcHV0ZV0oaHR0cDovL3d3dy5iaW9jb25kdWN0b3Iub3JnL3BhY2thZ2VzL3JlbGVhc2UvYmlvYy9odG1sL2ltcHV0ZS5odG1sKSAgICAKKiBbaW1wdXRlTURSXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPWltcHV0ZU1EUikgICAgCiogW2ttaV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1rbWkpICAgIAoqIFttaV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1taSkgICAgICAgIAoqIFttaWNlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPW1pY2UpICAgICAKKiBbTUltaXhdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9TUltaXgpICAgCiogW21pc3NGb3Jlc3RdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9bWlzc0ZvcmVzdCkgICAKKiBbTWlzc2luZ0RhdGFHdWldKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9TWlzc2luZ0RhdGFHVUkpICAgCiogW21pc3NNREFdKGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9bWlzc01EQSkgICAKKiBbbWl0b29sc10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1taXRvb2xzKSAgIAoqIFttaXhdKGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9bWl4KSAgIAoqIFttdHNkaV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1tdHNkaSkgICAKKiBbbm9ybV0oaHR0cHM6Ly9DUkFOLlItcHJvamVjdC5vcmcvcGFja2FnZT1ub3JtKSAgIAoqIFtwYW5dKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3BhY2thZ2U9cGFuKSAgICAKKiBbcm9iQ29tcG9zaXRpb25zXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPXJvYkNvbXBvc2l0aW9ucykgICAKKiBbcnJjb3ZOQV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1ycmNvdk5BKSAgICAKKiBbc2JnY29wXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPXNiZ2NvcCkKKiBbVklNXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPVZJTSkgICAgCiogW3lhSW1wdXRlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy9wYWNrYWdlPXlhSW1wdXRlKSAgIAoqIFtaZWxpZ10oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvcGFja2FnZT1aZWxpZykgICAKCiMjIyMgVGhlIERhdGEgRnJhbWUKQSBkYXRhIGZyYW1lIGlzIGEgbGlzdCBpbiB3aGljaCBlYWNoIHJvdyBtYXkgcmVwcmVzZW50IGFuIG9ic2VydmF0aW9uIGFuZCBlYWNoIGNvbHVtbiBhIHZhcmlhYmxlLCBpcyBhIG5hdHVyYWwgZGF0YSBzdHJ1Y3R1cmUgZm9yIHN0YXRpc3RpY2FsIGFuYWx5c2lzLiBUaGlzIGNvbnNpc3RlbnQsIHViaXF1aXRvdXMgZGF0YSBzdHJ1Y3R1cmUgKipwcm92aWRlcyBSIHdpdGggYSBiaWcgdGVjaG5pY2FsIGFkdmFudGFnZSoqLiBUaGlzIGlzIG5vIGFjY2lkZW50LiBCb3RoIFJvYmVydCBHZW50bGVtYW4gYW5kIFJvc3MgSWhha2EgYmVsaWV2ZWQgaW4gW05pa2xhdXMgV2lydGjigJlzXShodHRwczovL2VuLndpa2lwZWRpYS5vcmcvd2lraS9OaWtsYXVzX1dpcnRoKSBkaWN0dW06IFtgYWxnb3JpdGhtcyArIGRhdGEgc3RydWN0dXJlcyA9IHByb2dyYW1zYF0oaHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvQWxnb3JpdGhtc18lMkJfRGF0YV9TdHJ1Y3R1cmVzXyUzRF9Qcm9ncmFtcykuCgojIyAqX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXyogCgojIyMgVGhlIFIgUGFja2FnZSBTeXN0ZW0KVGhlcmUgYXJlIG92ZXIgMTEsNTAwIHBhY2thZ2VzIG9uIENSQU4sIFIncyBjZW50cmFsIHJlcG9zaXRvcnkuIFRoZSBhbGdvcml0aG1zIGluIHRoZXNlIHBhY2thZ2VzIGNvbXByaXNlIGEgdHJhbnNwYXJlbnQsIGRvY3VtZW50ZWQgc3RhdGlzdGljYWwgcmVzb3VyY2Ugb2YgZ3JlYXQgdmFsdWUuIFNlYXJjaGluZyB0aGUgdGhyb3VnaCBDUkFOIGlzIGEgY2hhbGxlbmdlLCBidXQgdGhlIFtDUkFOIFRhc2sgVmlld3NdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi92aWV3cy8pIGxpc3RzIG9mIFIgcGFja2FnZXMsIGN1cmF0ZWQgYnkgZXhwZXJ0cyBhbmQgb3JnYW5pemVkIGJ5IGFwcGxpY2F0aW9uIGFyZWEgbWl0aWdhdGUgdGhlIHByb2JsZW0uCgohW10odGFza3ZpZXdzLnBuZykKIyMgKl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18qIAoKIyMgVG9vbHMgZm9yIFdvcmtpbmcgaW4gUgoKTGV0J3MgdGFrZSBhIGZldyBtaW51dGVzIHRvIGV4YW1pbmUgdGhpcyBub3RlYm9vayBhbmQgdGhlIFIgTWFya2Rvd24gY29kZS4KCiMjICpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fKiAKCiMjIFRvb2xzIGZvciBNYW5pcHVsYXRpbmcgRGF0YQojIyMgVGhlIFRpZHl2ZXJzZQpBbm90aGVyIGdyZWF0IGFkdmFudGFnZSBvZiBSIGlzIGl0cyBhYmlsaXR5IHRvIGZhY2lsaXRhdGUgdGhlIGNvbnN0cnVjdGlvbiBvZiBEb21haW4gU3BlY2lmaWMgTGFuZ3VhZ2VzLiBBcyBTaGlueSdzIGNyZWF0b3IgSm9lIENoZW5nIHB1dHMgaXQgWyhJbnRlcmlldyB3aXRoIEpvZSBDaGVuZyldKGh0dHBzOi8vcnZpZXdzLnJzdHVkaW8uY29tLzIwMTcvMDEvMDQvaW50ZXJ2aWV3LXdpdGgtam9lLWNoZW5nLyk6Cgo+IElmIHlvdSBsb29rIGJleW9uZCB0aGUgc3ludGF4LCBSIHJlYWxseSBpcyBjb25jZXB0dWFsbHkgdmVyeSBtdWNoIGxpa2UgTGlzcCBpbiBhIGxvdCBvZiB3YXlzLiBPbmUgb2YgdGhvc2Ugd2F5cyBpcyB0aGF0IGl0IG1ha2VzIGl0IHZlcnksIHZlcnkgZWFzeSB0byBjb21wdXRlIG9uIHRoZSBwcm9ncmFtbWluZyBsYW5ndWFnZSBpdHNlbGYuIC4uLgoKPiBJIHRoaW5rIGRheS10by1kYXksIFIgcHJvZ3JhbW1lcnMgcHJvYmFibHkgZG9u4oCZdCB0aGluayBhYm91dCB0aGVzZSB0aGluZ3MsIGJ1dCB0aGUgZWxlZ2FudCwgdGVyc2Ugc3ludGF4IG9mIGRwbHlyIGFuZCB0aGUgcGlwZSBvcGVyYXRvciBhcmUgcG9zc2libGUgYmVjYXVzZSBvZiBob3cgbWFsbGVhYmxlIGEgbGFuZ3VhZ2UgUiBpcyBhbmQgaG93IGdyZWF0IGl0IGlzIGZvciB3cml0aW5nIERTTHMgaW4gaXQuCgo+IFBlcnNvbmFsbHksIG9uZSBvZiBteSBwZXQgcGVldmVzIGR1cmluZyB0aGVzZSBsYW5ndWFnZSB3YXJzIGlzIHdoZW4gcGVvcGxlIHNheSB0aGF0IG9uZSBvZiB0aGUgZGllcmVuY2VzIGJldHdlZW4gc2F5IFB5dGhvbiBvciBKdWxpYSBhbmQgUiBpcyB0aGF0IFIgaXMgYSBEU0wgZm9yIHN0YXRzLCB3aGVyZWFzIHRoZXNlIG90aGVyIHRoaW5ncyBhcmUgZ2VuZXJhbCBwdXJwb3NlIGxhbmd1YWdlcy4gUiBpcyBub3QgYSBEU0wuIEl04oCZcyBhIGxhbmd1YWdlIGZvciB3cml0aW5nIERTTHMsIHdoaWNoIGlzIHNvbWV0aGluZyB0aGF04oCZcyBhbHRvZ2V0aGVyIG1vcmUgcG93ZXJmdWwuIEkgYWN0dWFsbHkgdGhpbmsgdGhhdCBKdWxpYSBoYXMgbWFueSBvZiB0aGVzZSBzYW1lIGNoYXJhY3RlcmlzdGljcywgYnV0IFB5dGhvbiwgZXZlbiB0aG91Z2ggaXQgb2J2aW91c2x5IGhhcyBpdHMgb3duIHN0cmVuZ3RocywgY2VydGFpbmx5IGRvZXNu4oCZdCBzaGFyZSB0aGF0IHNhbWUgbGV2ZWwgb2YgZmV4aWJpbGl0eS4KCk9uZSB3YXkgdG8gdGhpbmsgb2YgdGhlIFtUaWR5dmVyc2VdKGh0dHBzOi8vd3d3LnRpZHl2ZXJzZS5vcmcvKSBpcyB0aGF0IGl0IGlzIGEgRFNMIGZvciBkb2luZyBkYXRhIHNjaWVuY2UuIFRoZSBwYWNrYWdlcyB0aGF0IGNvbXByaXNlIHRoZSB0aWR5dmVyc2Ugb2ZmZXIgYSBjb25zaXN0ZW50LCBpbnRlZ3JhdGVkIHNldCBvZiBmdW5jdGlvbnMgZm9yIGltcGxlbWVudGluZyB0aGUgY2Fub25pY2FsIGRhdGEgc2NpZW5jZSB3b3JrZmxvdy4KCiFbXSh0aWR5dmVyc2UucG5nKQo8YnIvPgo8YnIvPgpUaGUgbmFtZSAidGlkeXZlcnNlIiBjb21lcyBmcm9tIHRoZSBjb25jZXB0IG9mICJ0aWR5IiBkYXRhIHdoaWNoIEhhZGxleSBXaWNraGFtLCB0aGUgdGlkeXZyZXNlJ3MgcHJpbmNpcGFsIGFyY2hpdGVjdCBhbmQgaW1wbGVtZW50ZXIsIGRlc2NyaWJlcyBhcyBmb2xsb3dzOiAKCj4gW1RpZHkgZGF0YV0oaHR0cDovL3ZpdGEuaGFkLmNvLm56L3BhcGVycy90aWR5LWRhdGEucGRmKSBpcyBhIHN0YW5kYXJkIHdheSBvZiBtYXBwaW5nIHRoZSBtZWFuaW5nIG9mIGEgZGF0YXNldCB0byBpdHMgc3RydWN0dXJlLiBBIGRhdGFzZXQgaXMgbWVzc3kgb3IgdGlkeSBkZXBlbmRpbmcgb24gaG93IHJvd3MsIGNvbHVtbnMgYW5kIHRhYmxlcyBhcmUgbWF0Y2hlZCB1cCB3aXRoIG9ic2VydmF0aW9ucywgdmFyaWFibGVzIGFuZCB0eXBlcy4gSW4gdGlkeSBkYXRhOiAoMSkgRWFjaCB2YXJpYWJsZSBmb3JtcyBhIGNvbHVtbiwgKDIpIEVhY2ggb2JzZXJ2YXRpb24gZm9ybXMgYSByb3cgYW5kICgzKSBFYWNoIHR5cGUgb2Ygb2JzZXJ2YXRpb25hbCB1bml0IGZvcm1zIGEgdGFibGUuCgojIyMjIE1hbmlwdWxhdGluZyBhbmQgVHJhbnNmb3JtaW5nIERhdGEKVGhlIGBkcGx5cmAgcGFja2FnZSB3aGljaCBwcm92aWRlcyBhIGNvbnNpc3RlbnQgc2V0IG9mIHZlcmJzIGZvciBtYW5pcHVsYXRpbmcgZGF0YSBwcm92aWRlcyB0aGUgYmFja2JvbmUgc3RydWN0dXJlIGZvciB0aGUgaW1wb3J0LCB0eWR5IGFuZCB0cmFuc2Zvcm0gc2VxdWVuY2Ugb2YgdGhlIGRhdGEgc2NpZW5jZSB3b3JrZmxvdy4gVGhlIGJhc2ljIGBkcGx5cmAgdmVyYnMgYXJlOgoqIGBmaWx0ZXIoKWAgdG8gc2VsZWN0IGNhc2VzIGJhc2VkIG9uIHRoZWlyIHZhbHVlcyAgICAKKiBgYXJyYW5nZSgpYCB0byByZW9yZGVyIHRoZSBjYXNlcyAgIAoqIGBzZWxlY3QoKWAgYW5kIGByZW5hbWUoKWAgdG8gc2VsZWN0IHZhcmlhYmxlcyBiYXNlZCBvbiB0aGVpciBuYW1lcyAgIAoqIGBtdXRhdGUoKWAgYW5kIGB0cmFuc211dGUoKWAgdG8gYWRkIG5ldyB2YXJpYWJsZXMgdGhhdCBhcmUgZnVuY3Rpb25zIG9mIGV4aXN0aW5nIHZhcmlhYmxlcyAgICAKKiBgc3VtbWFyaXNlKClgICB0byBjb25kZW5zZSBtdWx0aXBsZSB2YWx1ZXMgdG8gYSBzaW5nbGUgdmFsdWUgICAgCiogYHNhbXBsZV9uKClgIGFuZCBgc2FtcGxlX2ZyYWMoKWAgIHRvIHRha2UgcmFuZG9tIHNhbXBsZXMKCkhlcmUgd2Ugd2lsbCBsb29rIGF0IGEgZmV3IGV4YW1wbGVzIG9mIHdvcmtpbmcgd2l0aCB0aGVzZSB2ZXJicyB1c2luZyBueWNmbGlnaHRzMTM6OmZsaWdodHMsIGEgZGF0YXNldCBjb250YWluaW5nIGFsbCAzMzY3NzYgZmxpZ2h0cyB0aGF0IGRlcGFydGVkIGZyb20gTmV3IFlvcmsgQ2l0eSBpbiAyMDEzLiBUaGUgZGF0YSBjb21lcyBmcm9tIHRoZSBbVVMgQnVyZWF1IG9mIFRyYW5zcG9ydGF0aW9uIFN0YXRpc3RpY3NdKGh0dHBzOi8vd3d3LnRyYW5zdGF0cy5idHMuZ292L0RhdGFiYXNlSW5mby5hc3A/REJfSUQ9MTIwJkxpbms9MCksIGFuZCBpcyBkb2N1bWVudGVkIGluID9ueWNmbGlnaHRzMTMKCmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygidGlkeXZlcnNlIiwibnljZmxpZ2h0czEzIikKbGlicmFyeShueWNmbGlnaHRzMTMpCmxpYnJhcnkodGlkeXZlcnNlKQpkYXRhKGZsaWdodHMpCmZsaWdodHMKYGBgCgoKSW4gdGhpcyBmaXJzdCBleGFtcGxlLCB3ZSB1c2UgYGRwbHlyOjpmaWx0ZXIoKWAgdG8gZmV0Y2ggZmxpZ2h0cyB0aGF0IGFycml2ZWQgZWFybHkgaW4gSmFudWFyeS4KCmBgYHtyfQojIGZpbHRlciBieSBkZXBhcnR1cmUgZGVsYXkgYW5kIHByaW50IHRoZSBmaXJzdCBmZXcgcmVjb3JkcwpmbGlnaHRzICU+JSBmaWx0ZXIobW9udGggPT0gMSwgZGVwX2RlbGF5IDwgMCkKYGBgCgpgYXJyYW5nZSgpYCByZW9yZGVycyByb3dzLiBJdCB0YWtlcyBhIGRhdGEgZnJhbWUsIGFuZCBhIHNldCBvZiBjb2x1bW4gbmFtZXMgKG9yIG1vcmUgY29tcGxpY2F0ZWQgZXhwcmVzc2lvbnMpIHRvIG9yZGVyIGJ5LiBJZiB5b3UgcHJvdmlkZSBtb3JlIHRoYW4gb25lIGNvbHVtbiBuYW1lLCBlYWNoIGFkZGl0aW9uYWwgY29sdW1uIHdpbGwgYmUgdXNlZCB0byBicmVhayB0aWVzIGluIHRoZSB2YWx1ZXMgb2YgcHJlY2VkaW5nIGNvbHVtbnM6CgpgYGB7cn0KYXJyYW5nZShmbGlnaHRzLCBzY2hlZF9kZXBfdGltZSwgZGVwX2RlbGF5KQoKYGBgCgpgc2VsZWN0KClgIGFsbG93cyB5b3UgdG8gc2VsZWN0IGNvbHVtbnMgYnkgbmFtZS4gSGVyZSB3ZSBzZWxlY3QgdGhlIGNvbHVtbnMgYG9yaWdpbmAsIGBkZXN0YCBhbmQgYGFpcl90aW1lYCwgYW5kIHRoZW4gc29ydCBieSBgYWlyX3RpbWVgIGZyb20gbGFyZ2VzdCB0byBzbWFsbGVzdC4KCmBgYHtyfQpzZWxlY3QoZmxpZ2h0cywgb3JpZ2luLCBkZXN0LCBhaXJfdGltZSkgJT4lIGFycmFuZ2UoZGVzYyhhaXJfdGltZSkpCmBgYAoKYG11dGF0ZSgpYCBhbGxvd3MgeW91IHRvIGFkZCBhIG5ldyBjb2x1bW4gdG8gYSBkYXRhIGZyYW1lLiBIZXJlIHdlIGFkZCBpbiBhIGNvbHVtbiBmb3IgYWlyc3BlZWQuCgpgYGB7cn0KbXV0YXRlKGZsaWdodHMsCiAgZ2FpbiA9IGFycl9kZWxheSAtIGRlcF9kZWxheSwKICBzcGVlZCA9IGRpc3RhbmNlIC8gYWlyX3RpbWUgKiA2MAopCmBgYAoKYHN1bW1hcmlzZSgpYCB3aWxsIGNvbXB1dGUgYSBzdGF0aXN0aWMgYW5kIGNvbGxhcHNlIGEgZGF0YSBmcmFtZSBpbnRvIGEgc2luZ2xlIHJvdy4KCmBgYHtyfQpkcGx5cjo6c3VtbWFyaXNlKGZsaWdodHMsCiAgZGVsYXkgPSBtZWFuKGRlcF9kZWxheSwgbmEucm0gPSBUUlVFKQopCmBgYAoKVGhlIGBncm91cF9ieSgpYCBmdW5jdGlvbnMgYWxsb3dzIHlvdSB1c2UgdGhlIHZlcmJzIGRpc2N1c3NlZCBhYm92ZSBvbiBncm91cHMgb2Ygb2JzZXJ2YXRpb25zIGluIGEgZGF0YXNldC4gSGVyZSB3ZSBjb21wdXRlIG1lYW4gZGlzdGFuY2UgYW5kIGFycml2YWwgZGVsYXkgZm9yIGVhY2ggaW5kaXZpZHVhbCBwbGFuZSBhbmQgcGxvdCB0aGUgcmVzdWx0cy4KCmBgYHtyLHdhcm5pbmc9RkFMU0V9CmJ5X3RhaWxudW0gPC0gZ3JvdXBfYnkoZmxpZ2h0cywgdGFpbG51bSkKZGVsYXkgPC0gZHBseXI6OnN1bW1hcmlzZShieV90YWlsbnVtLAogIGNvdW50ID0gbigpLAogIGRpc3QgPSBtZWFuKGRpc3RhbmNlLCBuYS5ybSA9IFRSVUUpLAogIGRlbGF5ID0gbWVhbihhcnJfZGVsYXksIG5hLnJtID0gVFJVRSkpCmRlbGF5IDwtIGZpbHRlcihkZWxheSwgY291bnQgPiAyMCwgZGlzdCA8IDIwMDApCgojIHBsb3QgZGVsYXlzCmxpYnJhcnkoZ2dwbG90MikKZ2dwbG90KGRlbGF5LCBhZXMoZGlzdCwgZGVsYXkpKSArCiAgZ2VvbV9wb2ludChhZXMoc2l6ZSA9IGNvdW50KSwgYWxwaGEgPSAxLzIpICsKICBnZW9tX3Ntb290aCgpICArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNCkKYGBgCgoKCkZvciBhIG1vcmUgY29tcGxldGUgcHJlc2VudGF0aW9uIHVzaW5nIHRoaXMgZGF0YSBzZWUgdGhlIFtkcGx5ciB2aWduZXR0ZV0oaHR0cHM6Ly9jcmFuLnItcHJvamVjdC5vcmcvd2ViL3BhY2thZ2VzL2RwbHlyL3ZpZ25ldHRlcy9kcGx5ci5odG1sKS4KCiMjICpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fKiAKCgojIyBUb29scyBmb3IgQWNjZXNzaW5nIERhdGEKIyMjIEFjY2Vzc2luZyBEYXRhYmFzZXMKV2l0aCB0aGUgYWlkIG9mIHRoZSBgREJJYCBwYWNrYWdlLCBgZHBseWAgaXMgYWJsZSB0byBpbXBvcnQgZGF0YSBmcm9tIHNldmVyYWwgZGlmZmVyZW50IG9wZW4gc291cmNlIGRhdGEgYmFzZXMgaW5jbHVkaW5nICpNeVNRTCogYW5kICpNYXJpYURCKiB3aXRoIHRoZSBbYFJNYXJpYURCYF0oaHR0cHM6Ly9naXRodWIuY29tL3JzdGF0cy1kYi9STWFyaWFEQikgcGFja2FnZSwgKlBvc3RncmVzKiBhbmQgKlJlZHNoaWZ0KiB3aXRoIFtgUlBvc3RncmVzc1NRTGBdKGh0dHBzOi8vY3Jhbi5yLXByb2plY3Qub3JnL3dlYi9wYWNrYWdlcy9SUG9zdGdyZVNRTC8pLCBhbmQgKlNRTGl0ZSogd2l0aCBbYFJTUUxpdGVgXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvUlNRTGl0ZS9pbmRleC5odG1sKS4gCgpUaGUgZm9sbG93aW5nIHNpbXBsZSBleGFtcGxlIGlsbHVzdHJhdGVzIHdvcmtpbmcgd2l0aCBTUUxpdGUuIEFnYWluLCB3ZSB1c2UgdGhlIG55Y2ZsaWdodHMxMyBkYXRhIHNldCB0byBjcmVhdGUgYSBTUUxpdGUgdGFibGUgd2l0aCAxLDAwMCByb3dzIGFuZCAxOSBjb2x1bW5zLgpgYGB7cn0KbGlicmFyeSh0aWR5dmVyc2UpCmxpYnJhcnkoREJJKQpsaWJyYXJ5KFJTUUxpdGUpCmxpYnJhcnkoZGJwbHlyKQpsaWJyYXJ5KG55Y2ZsaWdodHMxMykKCmNvbiA8LSBEQkk6OmRiQ29ubmVjdChSU1FMaXRlOjpTUUxpdGUoKSwgcGF0aCA9ICI6bWVtb3J5OiIpCgpjb3B5X3RvKGNvbiwgbnljZmxpZ2h0czEzOjpmbGlnaHRzLCAiZmxpZ2h0cyIsCiAgdGVtcG9yYXJ5ID0gRkFMU0UsIAogIGluZGV4ZXMgPSBsaXN0KAogICAgYygieWVhciIsICJtb250aCIsICJkYXkiKSwgCiAgICAiY2FycmllciIsIAogICAgInRhaWxudW0iLAogICAgImRlc3QiCiAgKQopCgpmbGlnaHRzX2RiIDwtIHRibChjb24sICJmbGlnaHRzIikKZmxpZ2h0c19kYiAKYGBgCgpOb3cgdGhhdCB3ZSBoYXZlIGVzdGFibGlzaGVkIGEgY29ubmVjdGlvbiB0byB0aGUgZGF0YWJhc2UgdGFibGUsIHdlIGNhbiB1c2UgdGhlIG5vcm1hbCBkcGx5ciB2ZXJicyB0byBxdWVyeSB0aGUgZGF0YS4KCmBgYHtyfQojIExpc3QgZGVwYXJ0dXJlIGRlbGF5IGFuZCBhcnJpdmFsIGRlbGF5IGZvciB5ZWFyIG1vbnRoIGFuZCBkYXkuCmZsaWdodHNfZGIgJT4lIHNlbGVjdCh5ZWFyOmRheSwgZGVwX2RlbGF5LCBhcnJfZGVsYXkpCgojIEZpbmQgcmVjb3JkcyB3aXRoIGRlcGFydHVyZSBkZWxheSBncmVhdGVyIHRoYW4gMjQwIG1pbnV0ZXMuCmZsaWdodHNfZGIgJT4lIGZpbHRlcihkZXBfZGVsYXkgPiAyNDApCgojIFN1bW1hcml6ZSBtZWFuIGRlcGFydHVyZSBkZWxheSBieSBkZXN0aW5hdGlvbiBhaXJwb3J0CmZsaWdodHNfZGIgJT4lIAogIGdyb3VwX2J5KGRlc3QpICU+JQogIHN1bW1hcmlzZShkZWxheSA9IG1lYW4oZGVwX3RpbWUpKQoKIyBkaXNjb25uZWN0IGZyb20gZGF0YWJhc2UKZGJEaXNjb25uZWN0KGNvbikKYGBgCgpgZHBseXJgIGNhbiBhbHNvIGJlIHVzZSB3aXRoIGRhdGEgc3RvcmVkIGluIGNvbW1lcmNpYWwgZGF0YWJhc2VzLiBUaGUgW2BiaWdxdWVyeWBdKGh0dHBzOi8vQ1JBTi5SLXByb2plY3Qub3JnL3BhY2thZ2U9YmlncnF1ZXJ5KSBlbmFibGVzIHdvcmtpbmcgd2l0aCBkYXRhIGluIEdvb2dsZSdzIFtCaWdRdWVyeV0oaHR0cHM6Ly9jbG91ZC5nb29nbGUuY29tL2JpZ3F1ZXJ5LykgcGxhdGZvcm0sIGFuZCB0aGUgW2BvZGJjYF0ocGFja2FnZSkgcGVybWl0cyBtYW55IGNvbW1lcmNpYWwgZGF0YWJhc2VzIHRvIGJlIHVzZWQgYXMgYSBgZHBseXJgIGJhY2tlbmQgdGhyb3VnaCB0aGUgb3BlbiBkYXRhYmFzZSBjb25uZWN0aXZpdHkgcHJvdG9jb2wsCgpGb3IgYWRkaXRpb25hbCBpbmZvcm1hdGlvbiBvbiBiYXNpYyBgZGJwbHlyYCBmdW5jdGlvbmFsaXR5IHNlZSB0aGUgW3ZpZ25ldHRlXShodHRwczovL2NyYW4uci1wcm9qZWN0Lm9yZy93ZWIvcGFja2FnZXMvZGJwbHlyL3ZpZ25ldHRlcy9kYnBseXIuaHRtbCkuIEZvciBhbiBhZHZhbmNlZCB0cmVhdG1lbnQgdXNpbmcgYGRwbHlyYCB3aXRoIGNvbW1lcmNpYWwgZ3JhZGUgZGF0YWJhc2VzIHNlZSB0aGUgdGhyZWUgYmxvZyBwb3N0cyBmcm9tIEVkZ2FyIFJ1aXo6ICAgIAoqIFtEYXRhYmFzZXMgdXNpbmcgUl0oaHR0cHM6Ly9ydmlld3MucnN0dWRpby5jb20vMjAxNy8wNS8xNy9kYXRhYmFzZXMtdXNpbmctci8pICAgCiogW1Zpc3VhbGl6YXRpb25zIHdpdGggUiBhbmQgRGF0YWJhc2VzXShodHRwczovL3J2aWV3cy5yc3R1ZGlvLmNvbS8yMDE3LzA4LzE2L3Zpc3VhbGl6YXRpb25zLXdpdGgtci1hbmQtZGF0YWJhc2VzLykgCiogW0RhdGFiYXNlIFF1ZXJpZXMgd2l0aCBSXShodHRwczovL3J2aWV3cy5yc3R1ZGlvLmNvbS8yMDE3LzEwLzE4L2RhdGFiYXNlLXF1ZXJpZXMtd2l0aC1yLykKKiBbRW50ZXJwcmlzZS1yZWFkeSBkYXNoYm9hcmRzIHdpdGggU2hpbnkgYW5kIGRhdGFiYXNlc10oaHR0cHM6Ly9ydmlld3MucnN0dWRpby5jb20vMjAxNy8wOS8yMC9kYXNoYm9hcmRzLXdpdGgtci1hbmQtZGF0YWJhc2VzLykuCgoqKkJlIHN1cmUgdG8gY2F0Y2ggRWRnYXIncyB0YWxrICJEYXRhYmFzZXMgVXNpbmcgUiIgMTE6MzAgQU0gdG9tb3Jyb3cgaW4gcm9vbSBUUjIqKgoKIyMgKl9fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18qIAoKIyMgVG9vbHMgZm9yIFZpc3VhbGl6aW5nIERhdGEKIyMjIEphdmFzY3JpcHQgVmlzdWFsaXphdGlvbnMgd2l0aCBodG1sd2lkZ2V0cwoKVGhlcmUgYXJlIHRocmVlIG1ham9yIHBsb3R0aW5nIHN5c3RlbXMgaW4gUjogKDEpIGJhc2UgZ3JhcGhpY3MsICgyKSBsYXR0aWNlIGdyYXBoaWNzIGFuZCAoMykgZ2dwbG90MiwgYW4gZXhhbXBsZSBvZiB3aGljaCB3ZSBoYXZlIHNlZW4gYWJvdmUuIEFkZGl0aW9uYWxseSwgdGhlcmUgaXMgYSBzaWduaWZpY2FudCBhbW91bnQgb2YgZGV2ZWxvcG1lbnQgd29yayBnb2luZyBvbiB0byBhbGxvdyBSIHVzZXJzIHRvIHByb2R1Y2UgSmF2YVNjcmlwdCB2aXN1YWxpemF0aW9ucyBkaXJlY3RseSBmcm9tIFIuIEluIHRoaXMgc2VjdGlvbiB3ZSB3aWxsIHNlZSBhbiBleGFtcGxlIG9mIHdvcmtpbmcgd2l0aCBSJ3MgW2h0bWx3aWRnZXRzXShodHRwOi8vd3d3Lmh0bWx3aWRnZXRzLm9yZy8pIGxpYnJhcnkuCgpGb3IgdGhpcyBleGFtcGxlIHdlIHdpbGwgd29yayB3aXRoIHRoZSBodG1sd2lkZ2V0IGBkeWdyYXBoc2Agd2hpY2ggaXMgdXNlZnVsIGZvciBwcm9kdWNpbmcgaW50ZXJhY3RpdmUgdGltZSBzZXJpZXMgdmlzdWFsaXphdGlvbnMgYW5kIHVzZSBpdCB0byB2aXN1YWxpemUgdGhlIG1vbnRobHkgZmxvdyBmcm9tIHRoZSBOaWxlIFJpdmVyIGZyb20gMTg3MSB0aHJvdWdoIDE5ODQuCgpgYGB7cn0gIApsaWJyYXJ5KHByYWNtYSkgICAgICAgICAgICAgIyBmb3IgTmlsZSByaXZlciBkYXRhCmRhdGEobmlsZSkKaGVhZChuaWxlKQpgYGAKClRoaXMgZGF0YSBzZXQgaXMgbm90IHRpZHkuIEZvciBlYWNoIHllYXIsIHRoZSBpbmZvcm1hdGlvbiBhYm91dCBtb250aGx5IGZsb3cgaXMgc3ByZWFkIG92ZXIgc2V2ZXJhbCBjb2x1bW5zLiBUbyB0aWR5IHRoaW5ncyB1cCwgd2UgbmVlZCB0byByZXNoYXBlIHRoZSBkYXRhIGZyYW1lIGludG8gImxvbmcgZm9ybSIgd2hlcmUgdGhlIGNvbHVtbnMgY29tcHJpc2UgdGhlIHZhcmlhYmxlcyBZZWFyLCBNb250aCBhbmQgRmxvdy4gSW4gdGhlIGNvZGUgYmVsb3csIHRoZSBgdGlkeWAgZnVuY3Rpb24gYGdhdGhlcigpYCBhY2NvbXBsaXNoZXMgdGhpcy4gYGFycmFuZ2UoKWAgc29ydHMgdGhlIGRhdGEgYnkgWWVhciBhbmQgTW9udGgsIGFuZCBtdXRhdGUgYWRkcyBhIG5ldyBkYXRlIHZhcmlhYmxlIHRvIHRoZSBkYXRhIGZyYW1lLgoKYGBge3J9Cm5pbGVfbG9uZyA8LSBuaWxlICU+JSAKICAgICAgICAgICAgICAgICAgZ2F0aGVyKEphbjpEZWMsIGtleSA9ICJNb250aCIsIHZhbHVlID0gIkZsb3ciKSAlPiUgCiAgICAgICAgICAgICAgICAgIGFycmFuZ2UoWWVhcixtYXRjaChNb250aCxtb250aC5hYmIpKSAlPiUgCiAgICAgICAgICAgICAgICAgIGRwbHlyOjptdXRhdGUoCiAgICAgICAgICAgICAgICAgIERhdGUgPSBhcy5EYXRlKHBhc3RlKE1vbnRoLCItIiwiMTUiLCItIixhcy5jaGFyYWN0ZXIoWWVhciksc2VwPSIiKSwgZm9ybWF0PSIlYi0lZC0lWSIpKQogIApoZWFkKG5pbGVfbG9uZykKYGBgCgpOZXh0LCB3ZSBwcm9kdWNlIHNvbWUgZXhwbG9yYXRvcnkgcGxvdHMgd2l0aCBnZ3Bsb3QyCgpgYGB7cn0KIyBQbG90IHRoZSB0aW1lIHNlcmllcwpwIDwtIGdncGxvdChuaWxlX2xvbmdbMTAwOjMwMCxdLGFlcyh4PURhdGUseT1GbG93KSkKcCArIGdlb21fbGluZSgpICsgZ2VvbV9wb2ludChzaGFwZT0xLGNvbD0icmVkIikgKyAKICB5bGFiKCJGbG93IGluIGN1YmljIG1ldGVycyAvIHNlY29uZCIpICsgCiAgZ2d0aXRsZSgiTW9udGhseSBGbG93IG9mIE5pbGUgUml2ZXIgYXQgRG9uZ29sYSBTdGF0aW9uIikKCgojIEJveHBsb3RzIG9mIG1vbnRobHkgZmxvd3MKYiA8LSBnZ3Bsb3QobmlsZV9sb25nLGFlcyhmYWN0b3IoTW9udGgpLEZsb3cpKQpiICsgZ2VvbV9ib3hwbG90KCkgKwogIHhsYWIoIk1vbnRoIikgKwogIHlsYWIoIkZsb3cgaW4gY3ViaWMgbWV0ZXJzIC8gc2Vjb25kIikgKyAKICBnZ3RpdGxlKCJWYXJpYXRpb24gb2YgRmxvdyBhdCBEb25nb2xhIFN0YXRpb24gYnkgTW9udGgiKQpgYGAgICAKCldlIGZpbmlzaCB1cCBieSBjcmVhdGluZyBhbiBpbnRlcmFjdGl2ZSwgSmF2YVNjcmlwdCBEMyBpbnRlcmFjdGl2ZSBwbG90LiBUbyBnZXQgdGhlIGRhdGEgcmVhZHkgZm9yIHRoZSBgZHlncmFwaCgpYCBmdW5jdGlvbiB3ZSBtdXN0IG1ha2UgaXQgYW4gYHh0c2AgdGltZSBzZXJpZXMgb2JqZWN0LgoKYGBge3J9CiMgQ3JlYXRlIGFuIGludGVyYWN0aXZlIGdyYXBoIHdpdGggYSBKYXZhc2NyaXB0IGxpYnJhcnkKbGlicmFyeSh4dHMpCmxpYnJhcnkoZHlncmFwaHMpCiMgTWFrZSBpbnRvIGEgdGltZSBzZXJpZXMgb2JqZWN0Cm5pbGVfdHMgPC0geHRzKG5pbGVfbG9uZyRGbG93LAogICAgICAgICAgICAgICBvcmRlci5ieT1uaWxlX2xvbmckRGF0ZSwKICAgICAgICAgICAgICAgZnJlcXVlbmN5PTEyLHN0YXJ0PWMoMTg3MSwxKSkKCiMgUGxvdCB3aXQgaHRtbHdpZGdldCBkeWdyYXBoCmR5Z3JhcGgobmlsZV90cyx5bGFiPSJjdWJpYyBtIC8gcyIsIAogICAgICAgIG1haW49Ik5pbGUgTW9udGhseSBGbG93IERhdGEiKSAlPiUKICBkeVNlcmllcygiVjEiLGxhYmVsPSJGbG93IikgJT4lCiAgZHlSYW5nZVNlbGVjdG9yKGRhdGVXaW5kb3cgPSBjKCIxODcxLTAxLTAxIiwiMTk4NC0xMi0wMSIpKQpgYGAgICAKClRvIHNlZSBzb21lIHNwZWN0YWN1bGFyIHZpc3VhbGl6YXRpb25zIGhhdmUgYSBsb29rIGF0IHRoZSBbaHRtbHdpZGdldHMgZm9yIFIgZ2FsbGVyeV0oaHR0cDovL2dhbGxlcnkuaHRtbHdpZGdldHMub3JnLykuCgojIyAqX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXyoKCiMjIFByZWRpY3RpdmUgTW9kZWxpbmcgVG9vbHMKClNlZSB0aGUgc2VwYXJhdGUgY2FyZXQgbm90ZWJvb2suCgojIyAqX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fXyogIAoKCiMjIFRvb2xzIGZvciBCaWcgRGF0YSBQbGF0Zm9ybXMKU2VlIHRoZSBub3RlYm9va3M6CiogUl9LZXJhc19UZW5zb3JGbG93Ciogc3BhcmxreXIKCiMjICpfX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fKiAKCiMjIFRvb2xzIGZvciBTaGFyaW5nIFJlc3VsdHMKIyMgU2hpbnkKClNoaW55IGlzIGFuIG9wZW4gc291cmNlIFIgcGFja2FnZSB0aGF0IHByb3ZpZGVzIGFuIGVsZWdhbnQgYW5kIHBvd2VyZnVsIHdlYiBmcmFtZXdvcmsgZm9yIGJ1aWxkaW5nIHdlYiBhcHBsaWNhdGlvbnMgdXNpbmcgUi4gU2hpbnkgaGVscHMgeW91IHR1cm4geW91ciBhbmFseXNlcyBpbnRvIGludGVyYWN0aXZlIHdlYiBhcHBsaWNhdGlvbnMgd2l0aG91dCByZXF1aXJpbmcgSFRNTCwgQ1NTLCBvciBKYXZhU2NyaXB0IGtub3dsZWRnZS4gU2hpbnkgaGFzIGJlY29tZSBhbiBlc3NlbnRpYWwgY29tbXVuaWNhdGlvbiBwbGF0Zm9ybSBmb3Igc2V2ZXJhbCBlbnRlcnByaXNlIGRhdGEgc2NpZW5jZSBjb21wYW5pZXMuCgpTb21lIEVudGVycHJpc2UgTGV2ZWwgU2hpbnkgQXBwbGljYXRpb25zOiAgICAKKiBbTFJUIFNpZ25hbCBBbmFseXNpcyBmb3IgYSBEcnVnXShodHRwczovL29wZW5mZGEuc2hpbnlhcHBzLmlvL0xSVGVzdC8pIGZyb20gdGhlIEZEQQoKIVtdKGxydC5wbmcpCgoqIFtUaGUgU2VhQ2xhc3MgUiBQYWNrYWdlXShodHRwczovL3J2aWV3cy5yc3R1ZGlvLmNvbS8yMDE3LzEwLzIzL3RoZS1zZWFjbGFzcy1yLXBhY2thZ2UvKSBmcm9tIFNlYWdhdGUgVGVjaG5vbG9naWVzXShodHRwczovL3J2aWV3cy5yc3R1ZGlvLmNvbS8yMDE3LzEwLzIzL3RoZS1zZWFjbGFzcy1yLXBhY2thZ2UvKQo8YnIvPgohW10oc2VhY2xhc3MucG5nKQoKPGJyLz4KClNvbWUgIkhvdyB0byIgU2hpbnkgcG9zdHM6CiogW0VudGVycHJpc2UtcmVhZHkgZGFzaGJvYXJkcyB3aXRoIFNoaW55IGFuZCBkYXRhYmFzZXNdKGh0dHBzOi8vcnZpZXdzLnJzdHVkaW8uY29tLzIwMTcvMDkvMjAvZGFzaGJvYXJkcy13aXRoLXItYW5kLWRhdGFiYXNlcy8pCiogW1BvcnRmb2xpbyBWb2xhdGlsaXR5IFNoaW55IEFwcF0oaHR0cHM6Ly9ydmlld3MucnN0dWRpby5jb20vMjAxNy8wOC8wOS9wb3J0Zm9saW8tdm9sYXRpbGl0eS1zaGlueS1hcHAvKQoqIFtQcmludGluZyBGcm9tIEZsZXggRGFzaGJvYXJkXShodHRwczovL3J2aWV3cy5yc3R1ZGlvLmNvbS8yMDE3LzA2LzI4L3ByaW50aW5nLWZyb20tZmxleC1kYXNoYm9hcmQvKQoKVG8gZ2V0IHN0YXJ0ZWQgd2l0aCBTaGlueToKKiBbVGhlIFNoaW55IFNob3djYXNlXShodHRwczovL3d3dy5yc3R1ZGlvLmNvbS9wcm9kdWN0cy9zaGlueS9zaGlueS11c2VyLXNob3djYXNlLykKKiBbTGVhcm4gU2hpbnldKGh0dHA6Ly9zaGlueS5yc3R1ZGlvLmNvbS8pCiogW1NoaW55IEdhbGxlcnldKGh0dHA6Ly9zaGlueS5yc3R1ZGlvLmNvbS9nYWxsZXJ5LykKCgoK